#include "TcpServer.h"

#include <utility>

TcpServer::TcpServer(const std::string &ip, uint16_t port, int threadNum)
        : threadNum(threadNum), mainLoop(new EventLoop(true)),
          acceptor(mainLoop.get(), ip, port), threadPool(threadNum)
{
    mainLoop->onEpollTimeout = [this](EventLoop *loop) { EpollTimeout(loop); };
    acceptor.onConnection = [this](Socket &clientSock) { OnConnection(clientSock); };
    for (int i = 0; i < threadNum; i++) {
        subLoops.emplace_back(new EventLoop(false, 5, 10));
        subLoops[i]->onEpollTimeout = [this](EventLoop *loop) { EpollTimeout(loop); };
        subLoops[i]->onTimer = [this](int fd) { RemoveConnection(fd); };
        threadPool.Commit([this, i] { subLoops[i]->Run(); });
    }
}

TcpServer::~TcpServer()
{}

void TcpServer::Start()
{
    mainLoop->Run();
}

void TcpServer::Stop()
{
    mainLoop->Stop();
    for (int i = 0; i < threadNum; i++) {
        subLoops[i]->Stop();
    }
    threadPool.Stop();
}

void TcpServer::OnConnection(Socket &clientSock)
{
    uint32_t idx = clientSock.fd % threadNum;
    ConnectionPtr conn(new Connection(subLoops[idx].get(), std::move(clientSock)));
    conn->onClose = [this](ConnectionPtr ptr) { CloseConnection(std::move(ptr)); };
    conn->onError = [this](ConnectionPtr ptr) { ErrorConnection(std::move(ptr)); };
    conn->onMessage = [this](ConnectionPtr ptr, std::string &message) { OnMessage(std::move(ptr), message); };
    conn->onSendComplete = [this](ConnectionPtr ptr) { SendComplete(std::move(ptr)); };
    {
        std::lock_guard<std::mutex> lock(mutex);
        conns[conn->GetFd()] = conn;
    }
    subLoops[idx]->OnConnection(conn);
    if (onConnection) {
        onConnection(conn);
    }
}

void TcpServer::CloseConnection(ConnectionPtr conn)
{
    if (onCloseConnection) {
        onCloseConnection(std::move(conn));
    }
    std::lock_guard<std::mutex> lock(mutex);
    conns.erase(conn->GetFd());
}

void TcpServer::ErrorConnection(ConnectionPtr conn)
{
    if (onErrorConnection) {
        onErrorConnection(std::move(conn));
    }
    std::lock_guard<std::mutex> lock(mutex);
    conns.erase(conn->GetFd());
}

void TcpServer::OnMessage(ConnectionPtr conn, std::string &message) const
{
    std::cout << "message:" << message << std::endl;
    if (onMessage) {
        onMessage(std::move(conn), message);
    }
}

void TcpServer::SendComplete(ConnectionPtr conn) const
{
    if (onSendComplete) {
        onSendComplete(std::move(conn));
    }
}

void TcpServer::EpollTimeout(EventLoop *loop) const
{
    if (onTimeout) {
        onTimeout(loop);
    }
}

void TcpServer::RemoveConnection(int fd)
{
    {
        std::lock_guard<std::mutex> lock(mutex);
        conns.erase(fd);
    }
    if (onRemoveConnection) {
        onRemoveConnection(fd);
    }
}